Redux

Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 是一个简单的 js 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该有任何副作用。

Redux 分为三大部分,store, action , reducer。

工作流程是:

  1. view 用 actionCreator 创建一个 action,里面可能包含一些数据
  2. 使用 store 的 dispatch 方法将 action 传入 store
  3. store 将 action 与旧的 state 转发给 reducer
  4. reducer 深拷贝 state,并返回一个新的 state 给 store
  5. store 接收并更新 state
  6. 使用 store.subscribe 订阅更新,重新 render 组件

有两个非常突出的特点:

  • predictable 可预测性。可预测性是由于它大量使用 pure function 和 plain object 等概念(reducer 和 action creator 是 pure function, state 和 action 是 plain object),并且 state 是 immutable 的。 这对于项目的稳定性会是很好的保证。
  • 可扩展性。可扩展性则让我们可通过 middleware 定制 action 的处理,通过 reducer enhancer 扩展 reducer 等等。从而有了丰富的社区扩展和支持,比如异步处理,Form,router 同步, redu/ubdo ,性能问题(selecter), 工具支持等。

Redux 遵循三个基本原则

  1. 单一数据来源: 整个应用程序的状态存储在单个对象树中。单状态树可以更容易地跟踪随时间的变化并调试或检查应用程序。
  2. 状态是只读的: 改变状态的唯一方法是发出一个动作,一个描述发生的事情的对象。这可以确保视图和网络请求都不会直接写入状态。
  3. 使用纯函数进行更改: 要指定状态树如何通过操作进行转换,您可以编写 reducers。Reducers 只是纯函数,它将先前的状态和操作作为参数,并返回下一个状态。

使用 redux 的步骤

  1. 安装 redux
  2. 创建文件夹、reducers 文件夹(先创建)、action 文件夹、store.js 文件
  3. 可能 reducers 文件里面不只一个 reduce,所以要在 reducers 文件里面的 index.js 里面去导入一下 combineReducers
import { combineReducers } from "redux";
import cart from "./cart";

export default combineReducers({
  cart,
});

Store

Store 是一个对象,它保存了整个应用的 state。与此同时,Store 也承担以下职责:

  • 允许通过 getState() 访问 state
  • 运行通过 dispatch(action) 改变 state
  • 通过 subscribe(listener) 注册 listeners,通过 subscribe(listener) 返回的函数处理 listeners 的注销

只需要使用createStore()从它创建的模块中导出存储。此外,它不应污染全局窗口对象。

store = createStore(myReducer);
export default store;

Action

Actions是纯 JavaScript 对象或信息的有效负载,可将数据从您的应用程序发送到您的 Store。 Action 必须具有指示正在执行的操作类型的 type 属性。

例如,表示添加新待办事项的示例操作:

{
  type: ADD_TODO,
  text: 'Add todo item'
}

Reducer

一个 reducer 是一个纯函数,该函数以先前的 state 和一个 action 作为参数,并返回下一个 state。

redux 的另一个缺点是:reducer 要求每次返回一个新的对象引用。当需要修改的数据层级较深,reducer 写起来很难保证优雅。所以一般 redux 项目都会刻意的保持 store 的扁平化,没有深层级的数据,用 Object.assign 浅拷贝处理。

redux 缺点

重绘

redux 的缺点也是足够明显的。每一次 dispatch 事件之后都会导致整个虚拟 dom 至顶向下的重绘。重绘剪枝需要在 shouldComponentUpdate中完成,如果事件足够复杂, store 足够大,shouldComponentUpdate方法的剪枝粒度就不那么容易控制。

  • 一个组件所需要的数据,必须由父组件传过来,不能像 flux 中直接从 store 取。
  • 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。

react-redux

  • 使用 react-redux 中的<Provider>来绑定全局的一个 store;

  • 使用 react-redux 中的connect来创建容器组件。

  • redux connect state -> props

  • mapDispatchToProps action-> props

Last Updated:
Contributors: yiliang114